﻿using Api.Shared.MyPlc.Modbus.DataCon;
using System.IO.Ports;

namespace Api.Shared.MyPlc.Modbus
{

    public class ModbusRtu
    {

        #region 对象或属性

        //定义串口通信的对象
        private SerialPort MyCom = new SerialPort();

        //创建通信超时属性
        public int ReadTimeOut { get; set; } = 2000;
        public int WriteTimeOut { get; set; } = 2000;

        //读取返回报文超时时间
        public int ReceiveTimeOut { get; set; } = 2000;

        //字节顺序
        public DataFormat DataFormat { get; set; } = DataFormat.ABCD;



        //通信延时时间
        public int SleepTime { get; set; } = 20;

        #endregion

        #region 打开关闭串口
        /// <summary>
        /// 打开串口
        /// </summary>
        /// <param name="iBaudRate">波特率</param>
        /// <param name="iPortName">串口号</param>
        /// <param name="iDataBits">数据位</param>
        /// <param name="iParity">校验位</param>
        /// <param name="iStopBits">停止位</param>
        public void OpenMyCom(int iBaudRate, string iPortName, int iDataBits, Parity iParity, StopBits iStopBits)
        {
            //如果当前串口是打开的，先关闭一下
            if (MyCom.IsOpen)
            {
                MyCom.Close();
            }

            //串口通信对象实例化
            MyCom = new SerialPort(iPortName, iBaudRate, iParity, iDataBits, iStopBits);

            //设置超时时间
            MyCom.ReadTimeout = this.ReadTimeOut;
            MyCom.WriteTimeout = this.WriteTimeOut;

            //打开串口
            MyCom.Open();
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        public void CloseMyCom()
        {
            if (MyCom.IsOpen)
            {
                MyCom.Close();
            }
        }
        #endregion

        #region 读取输出线圈 功能码01H
        /// <summary>
        /// 读取输出线圈方法
        /// </summary>
        /// <param name="iDevAdd">从站地址</param>
        /// <param name="iAddress">起始地址</param>
        /// <param name="iLength">长度</param>
        /// <returns></returns>
        public byte[] ReadOutputStatus(int iDevAdd, int iAddress, int iLength)
        {
            //第一步：拼接报文

            ByteArray SendCommand = new ByteArray();

            SendCommand.Add(new byte[] { (byte)iDevAdd, 0x01, (byte)(iAddress / 256), (byte)(iAddress % 256) });
            SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
            SendCommand.Add(Crc16(SendCommand.array, 6));

            //第二步：发送报文  接受报文

            int byteLength;

            if (iLength % 8 == 0)
            {
                byteLength = iLength / 8;
            }
            else
            {
                byteLength = iLength / 8 + 1;
            }

            byte[] response = new byte[5 + byteLength];

            if (SendData(SendCommand.array, ref response))
            {
                //第三步：解析报文

                //验证：功能码+字节计数
                if (response[1] == 0x01 && response[2] == byteLength)
                {
                    return GetByteArray(response, 3, byteLength);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        #endregion

        #region 读取输入线圈 功能码02H
        /// <summary>
        /// 读取输入线圈
        /// </summary>
        /// <param name="iDevAdd">从站地址</param>
        /// <param name="iAddress">起始地址</param>
        /// <param name="iLength">长度</param>
        /// <returns></returns>
        public byte[] ReadInputStatus(int iDevAdd, int iAddress, int iLength)
        {
            //第一步：拼接报文

            ByteArray SendCommand = new ByteArray();

            SendCommand.Add(new byte[] { (byte)iDevAdd, 0x02, (byte)(iAddress / 256), (byte)(iAddress % 256) });
            SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
            SendCommand.Add(Crc16(SendCommand.array, 6));

            //第二步：发送报文  接受报文

            int byteLength = 0;
            if (iLength % 8 == 0)
            {
                byteLength = iLength / 8;
            }
            else
            {
                byteLength = iLength / 8 + 1;
            }

            byte[] response = new byte[5 + byteLength];

            if (SendData(SendCommand.array, ref response))
            {
                //第三步：解析报文

                if (response[1] == 0x02 && response[2] == byteLength)
                {
                    return GetByteArray(response, 3, byteLength);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        #endregion

        #region 读取保持寄存器  功能码03H

        public byte[] ReadKeepReg(int iDevAdd, int iAddress, int iLength)
        {
            //第一步：拼接报文

            ByteArray SendCommand = new ByteArray();

            SendCommand.Add(new byte[] { (byte)iDevAdd, 0x03, (byte)(iAddress / 256), (byte)(iAddress % 256) });
            SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
            SendCommand.Add(Crc16(SendCommand.array, 6));

            //第二步：发送报文  接受报文

            int byteLength = iLength * 2;

            byte[] response = new byte[5 + byteLength];

            if (SendData(SendCommand.array, ref response))
            {
                //第三步：解析报文

                if (response[1] == 0x03 && response[2] == byteLength && response.Length == byteLength + 5)
                {
                    return GetByteArray(response, 3, byteLength);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        #endregion

        #region 读取输入寄存器  功能码04H

        public byte[] ReadInputReg(int iDevAdd, int iAddress, int iLength)
        {
            //第一步：拼接报文

            ByteArray SendCommand = new ByteArray();

            SendCommand.Add(new byte[] { (byte)iDevAdd, 0x04, (byte)(iAddress / 256), (byte)(iAddress % 256) });
            SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
            SendCommand.Add(Crc16(SendCommand.array, 6));

            //第二步：发送报文  接受报文

            int byteLength = iLength * 2;

            byte[] response = new byte[5 + byteLength];

            if (SendData(SendCommand.array, ref response))
            {
                //第三步：解析报文

                if (response[1] == 0x04 && response[2] == byteLength)
                {
                    return GetByteArray(response, 3, byteLength);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        #endregion

        #region 强制单线圈 功能码05H

        public bool ForceCoil(int iDevAdd, int iAddress, bool SetValue)
        {

            //第一步：拼接报文

            ByteArray SendCommand = new ByteArray();

            SendCommand.Add(new byte[] { (byte)iDevAdd, 0x05, (byte)(iAddress / 256), (byte)(iAddress % 256) });
            if (SetValue)
            {
                SendCommand.Add(new byte[] { 0xFF, 0x00 });
            }
            else
            {
                SendCommand.Add(new byte[] { 0x00, 0x00 });
            }
            SendCommand.Add(Crc16(SendCommand.array, 6));

            //第二步：发送报文  接受报文

            byte[] response = new byte[8];

            if (SendData(SendCommand.array, ref response))
            {
                //第三步：验证报文
                return ByteArrayEquals(SendCommand.array, response);
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region 预置单个寄存器 功能码06H

        public bool PreSetSingleReg(int iDevAdd, int iAddress, short SetValue)
        {
            //第一步：拼接报文

            ByteArray SendCommand = new ByteArray();

            SendCommand.Add(new byte[] { (byte)iDevAdd, 0x06, (byte)(iAddress / 256), (byte)(iAddress % 256) });

            SendCommand.Add(GetByteArrayFrom16Bit(SetValue));

            SendCommand.Add(Crc16(SendCommand.array, 6));

            //第二步：发送报文  接受报文

            byte[] response = new byte[8];

            if (SendData(SendCommand.array, ref response))
            {
                //第三步：验证报文
                return ByteArrayEquals(SendCommand.array, response);
            }
            else
            {
                return false;
            }
        }

        public bool PreSetSingleReg(int iDevAdd, int iAddress, ushort SetValue)
        {
            //第一步：拼接报文

            ByteArray SendCommand = new ByteArray();

            SendCommand.Add(new byte[] { (byte)iDevAdd, 0x06, (byte)(iAddress / 256), (byte)(iAddress % 256) });

            SendCommand.Add(GetByteArrayFrom16Bit(SetValue));

            SendCommand.Add(Crc16(SendCommand.array, 6));

            //第二步：发送报文  接受报文

            byte[] response = new byte[8];

            if (SendData(SendCommand.array, ref response))
            {
                //第三步：验证报文
                return ByteArrayEquals(SendCommand.array, response);
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region 强制多线圈 功能码0FH

        public bool ForceMultiCoil(int iDevAdd, int iAddress, bool[] SetValue)
        {
            //第一步：拼接报文

            byte[] iSetValue = BoolArrayToByteArray(SetValue);
            ByteArray SendCommand = new ByteArray();

            SendCommand.Add(new byte[] { (byte)iDevAdd, 0x0F, (byte)(iAddress / 256), (byte)(iAddress % 256) });

            SendCommand.Add(new byte[] { (byte)(SetValue.Length / 256), (byte)(SetValue.Length % 256) });

            SendCommand.Add((byte)iSetValue.Length);

            SendCommand.Add(iSetValue);

            SendCommand.Add(Crc16(SendCommand.array, 7 + iSetValue.Length));

            //第二步：发送报文  接受报文

            byte[] response = new byte[8];

            if (SendData(SendCommand.array, ref response))
            {
                //第三步：报文验证   验证前6个字节是否正确，再验证CRC是否正确

                byte[] b = GetByteArray(response, 0, 6);

                byte[] crc = Crc16(b, 6);

                return ByteArrayEquals(GetByteArray(SendCommand.array, 0, 6), b) && crc[0] == response[6] && crc[1] == response[7];

            }

            return false;
        }

        #endregion

        #region 预置多个寄存器 功能码10H

        //浮点型   Int32  UInt32    浮点型数组   Int32数组  UInt32数组  浮点型/int16/uint16

        public bool PreSetMultiByteArray(int iDevAdd, int iAddress, byte[] SetValue)
        {
            if (SetValue == null || SetValue.Length == 0 || SetValue.Length % 2 == 1)
            {
                return false;
            }

            int RegLength = SetValue.Length / 2;

            //第一步：拼接报文

            ByteArray SendCommand = new ByteArray();

            SendCommand.Add(new byte[] { (byte)iDevAdd, 0x10, (byte)(iAddress / 256), (byte)(iAddress % 256) });

            //寄存器数量
            SendCommand.Add(new byte[] { (byte)(RegLength / 256), (byte)(RegLength % 256) });

            //字节数量
            SendCommand.Add((byte)SetValue.Length);

            //具体字节
            SendCommand.Add(SetValue);

            //CRC
            SendCommand.Add(Crc16(SendCommand.array, 7 + SetValue.Length));

            //第二步：发送报文  接受报文

            byte[] response = new byte[8];

            if (SendData(SendCommand.array, ref response))
            {
                //第三步：报文验证   验证前6个字节是否正确，再验证CRC是否正确

                byte[] b = GetByteArray(response, 0, 6);

                byte[] crc = Crc16(b, 6);

                return ByteArrayEquals(GetByteArray(SendCommand.array, 0, 6), b) && crc[0] == response[6] && crc[1] == response[7];
            }
            else
            {
                return false;
            }
        }

        public bool PreSetMultiReg(int iDevAdd, int iAddress, float SetValue)
        {
            return PreSetMultiByteArray(iDevAdd, iAddress, GetByteArrayFrom32Bit(SetValue));
        }

        public bool PreSetMultiReg(int iDevAdd, int iAddress, int SetValue)
        {
            return PreSetMultiByteArray(iDevAdd, iAddress, GetByteArrayFrom32Bit(SetValue));
        }

        public bool PreSetMultiReg(int iDevAdd, int iAddress, uint SetValue)
        {
            return PreSetMultiByteArray(iDevAdd, iAddress, GetByteArrayFrom32Bit(SetValue));
        }

        public bool PreSetMultiReg(int iDevAdd, int iAddress, float[] SetValue)
        {
            ByteArray bSetValue = new ByteArray();

            foreach (float item in SetValue)
            {
                bSetValue.Add(GetByteArrayFrom32Bit(item));
            }

            return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
        }

        public bool PreSetMultiReg(int iDevAdd, int iAddress, int[] SetValue)
        {
            ByteArray bSetValue = new ByteArray();

            foreach (int item in SetValue)
            {
                bSetValue.Add(GetByteArrayFrom32Bit(item));
            }

            return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
        }

        public bool PreSetMultiReg(int iDevAdd, int iAddress, short[] SetValue)
        {
            ByteArray bSetValue = new ByteArray();

            foreach (short item in SetValue)
            {
                bSetValue.Add(GetByteArrayFrom16Bit(item));
            }

            return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
        }

        public bool PreSetMultiReg(int iDevAdd, int iAddress, uint[] SetValue)
        {
            ByteArray bSetValue = new ByteArray();

            foreach (uint item in SetValue)
            {
                bSetValue.Add(GetByteArrayFrom32Bit(item));
            }

            return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
        }

        public bool PreSetMultiReg(int iDevAdd, int iAddress, ushort[] SetValue)
        {
            ByteArray bSetValue = new ByteArray();

            foreach (ushort item in SetValue)
            {
                bSetValue.Add(GetByteArrayFrom16Bit(item));
            }

            return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
        }


        #endregion

        #region 通用发送报文并接受
        private static readonly object lockObj = new object();
        /// <summary>
        /// 通用发送报文并接受
        /// </summary>
        /// <param name="sendByte"></param>
        /// <param name="response"></param>
        /// <returns></returns>
        private bool SendData(byte[] sendByte, ref byte[] response)
        {
            //上锁

            lock (lockObj)
            {
                try
                {
                    //发送报文
                    MyCom.Write(sendByte, 0, sendByte.Length);

                    //定义一个Buffer
                    byte[] buffer = new byte[1024];

                    //定义一个内存
                    MemoryStream ms = new MemoryStream();

                    //定义读取开始时间
                    DateTime start = DateTime.Now;

                    //【1】获取当前缓冲区的值，判断是否有值，如果有值，读取过来，放到内存中

                    //接着再去判断，如果当前缓冲区的值为0，说明读完了

                    //【2】如果每次读取都读不到，我们用超时时间来做判断

                    while (true)
                    {
                        Thread.Sleep(SleepTime);

                        if (MyCom.BytesToRead >= 1)
                        {
                            int spcount = MyCom.Read(buffer, 0, buffer.Length);
                            ms.Write(buffer, 0, spcount);
                        }
                        else
                        {
                            //判断是否超时
                            if ((DateTime.Now - start).TotalMilliseconds > this.ReceiveTimeOut)
                            {
                                ms.Dispose();
                                return false;
                            }
                            else if (ms.Length > 0)
                            {
                                break;
                            }
                        }

                    }

                    response = ms.ToArray();
                    ms.Dispose();

                    return true;
                }
                catch (Exception)
                {
                    return false;
                }

                finally
                {

                }
            }


        }

        #endregion

        #region 自定义截取字节数组

        /// <summary>
        /// 自定义截取字节数组
        /// </summary>
        /// <param name="dest">目标字节数组</param>
        /// <param name="offset">偏移</param>
        /// <param name="count">数量</param>
        /// <returns></returns>
        private byte[] GetByteArray(byte[] dest, int offset, int count)
        {
            byte[] res = new byte[count];
            if (dest != null && dest.Length >= offset + count)
            {
                for (int i = 0; i < count; i++)
                {
                    res[i] = dest[offset + i];
                }
                return res;
            }
            else
            {
                return null;
            }
        }


        #endregion

        #region 判断两个数组是否完全一样

        private bool ByteArrayEquals(byte[] b1, byte[] b2)
        {
            if (b1 == null || b2 == null) return false;
            if (b1.Length != b2.Length) return false;
            for (int i = 0; i < b1.Length; i++)
            {
                if (b1[i] != b2[i])
                {
                    return false;
                }
            }
            return true;
        }


        #endregion

        #region 布尔数组转换成字节数组

        private byte[] BoolArrayToByteArray(bool[] val)
        {
            if (val == null && val?.Length == 0) return null;

            int iByteArrLen = 0;

            if (val?.Length % 8 == 0)
            {
                iByteArrLen = val.Length / 8;
            }
            else
            {
                iByteArrLen = val.Length / 8 + 1;
            }

            byte[] result = new byte[iByteArrLen];

            //遍历每个字节
            for (int i = 0; i < iByteArrLen; i++)
            {
                int total = val.Length < 8 * (i + 1) ? val.Length - 8 * i : 8;

                //遍历当前字节的每个位赋值
                for (int j = 0; j < total; j++)
                {
                    result[i] = SetbitValue(result[i], j + 1, val[8 * i + j]);
                }
            }
            return result;
        }

        #endregion

        #region 字节某个位赋值

        private byte SetbitValue(byte data, int index, bool val)
        {
            if (index > 8 || index < 1)
                return 0;
            int v = index < 2 ? index : (2 << (index - 2));
            return val ? (byte)(data | v) : (byte)(data & ~v);
        }

        #endregion

        #region 16位类型转换成字节数组

        private byte[] GetByteArrayFrom16Bit(short SetValue)
        {
            byte[] ResTemp = BitConverter.GetBytes(SetValue);
            byte[] Res = new byte[2];

            switch (this.DataFormat)
            {
                case DataFormat.ABCD:
                case DataFormat.CDAB:
                    Res[0] = ResTemp[1];
                    Res[1] = ResTemp[0];
                    break;
                case DataFormat.BADC:
                case DataFormat.DCBA:
                    Res = ResTemp;
                    break;
                default:
                    break;
            }
            return Res;
        }

        private byte[] GetByteArrayFrom16Bit(ushort SetValue)
        {
            byte[] ResTemp = BitConverter.GetBytes(SetValue);
            byte[] Res = new byte[2];

            switch (this.DataFormat)
            {
                case DataFormat.ABCD:
                case DataFormat.CDAB:
                    Res[0] = ResTemp[1];
                    Res[1] = ResTemp[0];
                    break;
                case DataFormat.BADC:
                case DataFormat.DCBA:
                    Res = ResTemp;
                    break;
                default:
                    break;
            }
            return Res;
        }

        #endregion

        #region 32位类型转换成字节数组

        private byte[] GetByteArrayFrom32Bit(float SetValue)
        {
            byte[] ResTemp = BitConverter.GetBytes(SetValue);

            byte[] Res = new byte[4];

            switch (this.DataFormat)
            {
                case DataFormat.ABCD:
                    Res[0] = ResTemp[3];
                    Res[1] = ResTemp[2];
                    Res[2] = ResTemp[1];
                    Res[3] = ResTemp[0];
                    break;
                case DataFormat.CDAB:
                    Res[0] = ResTemp[1];
                    Res[1] = ResTemp[0];
                    Res[2] = ResTemp[3];
                    Res[3] = ResTemp[2];
                    break;
                case DataFormat.BADC:
                    Res[0] = ResTemp[2];
                    Res[1] = ResTemp[3];
                    Res[2] = ResTemp[0];
                    Res[3] = ResTemp[1];
                    break;
                case DataFormat.DCBA:
                    Res = ResTemp;
                    break;
            }
            return Res;
        }

        private byte[] GetByteArrayFrom32Bit(int SetValue)
        {
            byte[] ResTemp = BitConverter.GetBytes(SetValue);

            byte[] Res = new byte[4];

            switch (this.DataFormat)
            {
                case DataFormat.ABCD:
                    Res[0] = ResTemp[3];
                    Res[1] = ResTemp[2];
                    Res[2] = ResTemp[1];
                    Res[3] = ResTemp[0];
                    break;
                case DataFormat.CDAB:
                    Res[0] = ResTemp[1];
                    Res[1] = ResTemp[0];
                    Res[2] = ResTemp[3];
                    Res[3] = ResTemp[2];
                    break;
                case DataFormat.BADC:
                    Res[0] = ResTemp[2];
                    Res[1] = ResTemp[3];
                    Res[2] = ResTemp[0];
                    Res[3] = ResTemp[1];
                    break;
                case DataFormat.DCBA:
                    Res = ResTemp;
                    break;
            }
            return Res;
        }

        private byte[] GetByteArrayFrom32Bit(uint SetValue)
        {
            byte[] ResTemp = BitConverter.GetBytes(SetValue);

            byte[] Res = new byte[4];

            switch (this.DataFormat)
            {
                case DataFormat.ABCD:
                    Res[0] = ResTemp[3];
                    Res[1] = ResTemp[2];
                    Res[2] = ResTemp[1];
                    Res[3] = ResTemp[0];
                    break;
                case DataFormat.CDAB:
                    Res[0] = ResTemp[1];
                    Res[1] = ResTemp[0];
                    Res[2] = ResTemp[3];
                    Res[3] = ResTemp[2];
                    break;
                case DataFormat.BADC:
                    Res[0] = ResTemp[2];
                    Res[1] = ResTemp[3];
                    Res[2] = ResTemp[0];
                    Res[3] = ResTemp[1];
                    break;
                case DataFormat.DCBA:
                    Res = ResTemp;
                    break;
            }
            return Res;
        }


        #endregion

        #region  CRC校验

        private static readonly byte[] aucCRCHi = {
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40
         };
        private static readonly byte[] aucCRCLo = {
             0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
             0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
             0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
             0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
             0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
             0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
             0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
             0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
             0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
             0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
             0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
             0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
             0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
             0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
             0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
             0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
             0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
             0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
             0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
             0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
             0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
             0x41, 0x81, 0x80, 0x40
         };
        private byte[] Crc16(byte[] pucFrame, int usLen)
        {
            int i = 0;
            byte[] res = new byte[2] { 0xFF, 0xFF };

            UInt16 iIndex = 0x0000;

            while (usLen-- > 0)
            {
                iIndex = (UInt16)(res[0] ^ pucFrame[i++]);
                res[0] = (byte)(res[1] ^ aucCRCHi[iIndex]);
                res[1] = aucCRCLo[iIndex];
            }
            return res;
        }

        #endregion

    }
}

